import java.util.HashMap;
import java.io.*;
import java.net.*;

/**
 * This class runs as a thread and continuously listens for incoming messages.  When it
 * receives one, it handles it with the appropriate message handler.
 * @author Ari Sapon-White and David Cummings
 */
public class ReceiveWorker implements Runnable
{
	/** A hash map of message handlers for handling different types of messages. */
	private HashMap<String, MsgHandler> hm;
	private PostOffice pobox;
	private ServerSocket ssock;
	private boolean done;
	
	/**
	 * Constructs a ReceiveWorker with no message handlers.
	 */
	public ReceiveWorker(PostOffice po)
	throws IOException
	{	pobox = po;
		done = false;
		hm = new HashMap<String, MsgHandler>();
		ssock = new ServerSocket (pobox.getPort ());
	}
	
	/**
	 * Adds a new message handler to the hash map of message handlers.
	 * @param key the name of the message handler
	 * @param value the message handler to add
	 */
	public void addHandler(String key, MsgHandler value)
	{
		synchronized (hm) { hm.put(key, value); }
	}
	
	/**
	 * Retrieves a specific message handler.
	 * @param key the name of the message handler to retrieve
	 * @return the requested message handler
	 */
	public MsgHandler getHandler(String key)
	{	synchronized (hm) { return hm.get(key); }
	}
	
	/**
	 * Constantly listens for new messages and processes them by
	 * sending them to appropriate message handlers.
	 */
	@Override
	public void run()
	{
		while(!done)
		{	try
			{	Socket sock = ssock.accept ();
				ObjectInputStream ois = new ObjectInputStream (
					sock.getInputStream ());
				Message m = (Message)ois.readObject ();
				ois.close ();

				(new Thread (new DeliveryHandler (m))).start ();
			}
			catch (Exception e) // list exceptions with more time XXX
			{	System.err.println ("Error reading message: " + e); }
		} // while
	} // run

	class DeliveryHandler implements Runnable
	{	Message msg;
		public DeliveryHandler (Message m) { msg = m; }

		/** 
		 * Handle a message by reading its payload, and determine
		 * whether anyone has registered for this message, otherwise
		 * we deliver to the mailbox.
		 */
		public void run ()
		{	MsgHandler handler = null;
			synchronized (hm)
			{	handler = hm.get (msg.getMsg ()); }
			if (handler != null) 
			{	handler.handleMsg (msg); }
			else
			{	pobox.enqueueMsg (msg); }
		} // run
	} // DeliveryHandler
} // class ReceiveWorker
